
module Content
  include DrunkenMaster

  class Field < Widget

    def initialize *attrs
      super
      setup_fields
    end

    def static
      self.children = []
      if has_fields?
        fields.values.each { |f|
          f.children = []
          f.static
          add f
        }
      else
        add @value.to_s
      end
      self
    end

    def editable
      self.children = []
      if has_fields?
        editable_children
      else
        e :input, :type => 'text', :value => @value, :name => form_field_name
      end
      self
    end

    def editable_children
      fields.each do |name, field|
        field.field_name = name.to_s
        field.children = []
        field.editable
        add field
      end
    end

    def has_fields?
      self.class.field_definition.length > 0
    end

    def value
      if has_fields?
        Hash[*
          fields.collect { |k, f|
            [k, f.value]
          }.flatten
        ]
      else
        @value
      end
    end

    def value= value
      if has_fields?
        p "setting fields values #{value.inspect}"
        fields.collect { |k, f|
          f.value = value[k] || value[k.to_s]
        }
      else
        @value = value
      end
    end

    def self.field hash
      hash.each do |k, v|
        field_definition[k] = {
            :class => v
          }
        define_method(k) do
          fields[k].value
        end
        define_method(k.to_s+'=') do |value|
          fields[k].value= value
        end
      end
    end

    def fields
      @__field_fields
    end

    def field_name
      @__field_name
    end

    def field_name= name
      @__field_name = name
    end

    def form_field_name
      field_name
    end

    def setup_fields
      if has_fields?
        @__field_fields ||= {}
        self.class.field_definition.each do |name, info|
          @__field_fields[name] ||= info[:class].new
        end
      end
    end

    def self.field_definition
      if self.class == Class
        my_class = self
      else
        my_class = self.class
      end
      @@__fields ||= InsertOrderPreservingHash.new
      @@__fields[my_class] ||= {}
    end


    class InsertOrderPreservingHash
      include Enumerable

      def initialize(*args, &block)
        @h = Hash.new(*args, &block)
        @ordered_keys = []
      end

      def []=(key, val)
        @ordered_keys << key unless @h.has_key? key
        @h[key] = val
      end

      def each
        @ordered_keys.each {|k| yield(k, @h[k])}
      end
      alias :each_pair :each

      def each_value
        @ordered_keys.each {|k| yield(@h[k])}
      end

      def each_key
        @ordered_keys.each {|k| yield k}
      end

      def keys
        @ordered_keys
      end

      def values
        @ordered_keys.map {|k| @h[k]}
      end

      def clear
        @ordered_keys.clear
        @h.clear
      end

      def delete(k, &block)
        @ordered_keys.delete k
        @h.delete(k, &block)
      end

      def reject!
        del = []
        each_pair {|k,v| del << k if yield k,v}
        del.each {|k| delete k}
        del.empty? ? nil : self
      end

      def delete_if(&block)
        reject!(&block)
        self
      end

      %w(merge!).each do |name|
        define_method(name) do |*args|
          raise NotImplementedError, "#{name} not implemented"
        end
      end

      def method_missing(*args)
        @h.send(*args)
      end
    end


  end
end

